package cn.zn.modules.appoin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.zn.framework.common.exception.RRException;
import cn.zn.modules.appoin.dao.AppoinTimeMainMapper;
import cn.zn.modules.appoin.entity.*;
import cn.zn.modules.appoin.entity.dto.AppoinTimeMainSaveDTO;
import cn.zn.modules.appoin.entity.vo.*;
import cn.zn.modules.appoin.service.*;
import cn.zn.modules.zzsys.inter.TeacherInter;
import cn.zn.modules.zzsys.inter.vo.TeacherInfoVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beust.jcommander.internal.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AppoinTimeMainServiceImpl extends ServiceImpl<AppoinTimeMainMapper, AppoinTimeMain> implements AppoinTimeMainService {

    @Autowired
    private AppoinTimeDetailService appoinTimeDetailService;
    @Autowired
    private AppoinTimeRuleService appoinTimeRuleService;
    @Autowired
    private AtrWeekService atrWeekService;
    @Autowired
    private AtrWeekWorkTimeService atrWeekWorkTimeService;
    @Autowired
    private AtrSpecialService atrSpecialService;
    @Autowired
    private AtrSpecialWorkTimeService atrSpecialWorkTimeService;
    @Autowired
    private AtRuleService atRuleService;
    @Autowired
    private AtSpecialService atSpecialService;
    @Autowired
    private AtSpecialWorkTimeService atSpecialWorkTimeService;
    @Autowired
    private TeacherInter teacherInter;

    @Override
    public List<AppoinWorkUserVO> queryNoWorkByDate(String appoinDate, Map<String, Object> params) {
        // 当前总规则
        AppoinTimeRuleEntity appoinTimeRuleEntity = appoinTimeRuleService.queryUse();
        if (appoinTimeRuleEntity == null) {
            throw new RRException("未开启默认规则");
        }
        // 查询当天工作人员
        Map<String, AppoinMainWorkUser> appoinMainWorkUserMap = super.list(new QueryWrapper<AppoinTimeMain>()
                .eq("appoin_date_", appoinDate))
                .stream().collect(Collectors.toMap(AppoinTimeMain::getUserCode, o -> {
                    AppoinMainWorkUser appoinMainWorkUser = new AppoinMainWorkUser();
                    BeanUtils.copyProperties(o, appoinMainWorkUser);
                    return appoinMainWorkUser;
                }, (o1, o2) -> o1));
        // 查询某角色的用户（去掉当天有工作的人员）
        List<TeacherInfoVO> teacherInfoVOS = teacherInter.queryByRole(appoinTimeRuleEntity.getWorkerRole(), CollUtil.newArrayList(appoinMainWorkUserMap.keySet()));
        if (teacherInfoVOS.isEmpty()) {
            // 没有人员返回空集合
            return Lists.newArrayList();
        }
        // 查询用户的工作时间设置
        Map<String, AtRule> atRuleMap = atRuleService.list(new QueryWrapper<AtRule>().in("USER_CODE_", teacherInfoVOS.stream().map(TeacherInfoVO::getWorkNo).collect(Collectors.toList()))).stream().collect(Collectors.toMap(AtRule::getUserCode, o -> o, (o1, o2) -> o1));
        List<TeacherInfoVO> teacherInfoFilterVOS = teacherInfoVOS.stream().filter(o -> atRuleMap.containsKey(o.getWorkNo())).collect(Collectors.toList());
        Map<String, AppoinWorkUserVO> userInfoVOMap = teacherInfoFilterVOS.stream().collect(Collectors.toMap(TeacherInfoVO::getWorkNo, o -> {
            AppoinWorkUserVO appoinWorkUserVO = new AppoinWorkUserVO();
            BeanUtils.copyProperties(o, appoinWorkUserVO);
            appoinWorkUserVO.setUserCode(o.getWorkNo());
            return appoinWorkUserVO;
        }, (o1, o2) -> o1, LinkedHashMap::new));
        atRuleMap.forEach((k, v) -> {
            userInfoVOMap.get(k).setRuleId(v.getId());
            userInfoVOMap.get(k).setInterval(v.getInterval());
            userInfoVOMap.get(k).setIsAuto(v.getIsAuto());
        });
        return CollUtil.newArrayList(userInfoVOMap.values());
    }

    @Override
    public List<AppoinMainWorkUser> queryByDate(String appoinDate, Boolean isActive) {
        // 查询当天工作人员
        Map<String, AppoinMainWorkUser> appoinMainWorkUserMap = super.list(
                new QueryWrapper<AppoinTimeMain>()
                        .eq("APPOIN_DATE_", appoinDate)
                        .eq(isActive != null, "IS_ACTIVE_", isActive)
        ).stream().collect(Collectors.toMap(AppoinTimeMain::getUserCode, o -> {
            AppoinMainWorkUser appoinMainWorkUser = new AppoinMainWorkUser();
            BeanUtils.copyProperties(o, appoinMainWorkUser);
            return appoinMainWorkUser;
        }, (o1, o2) -> o1));
        if (appoinMainWorkUserMap.size() == 0) {
            return CollUtil.newArrayList();
        }
        // 工作人员名称
        teacherInter.findByWorkNos(CollUtil.newArrayList(appoinMainWorkUserMap.keySet()))
                .values().forEach(o -> {
            appoinMainWorkUserMap.get(o.getWorkNo()).setUserName(o.getName());
        });
        // 查询工作时间数量
        Map<String, AppoinMainWorkUser> workUserMap = appoinMainWorkUserMap.values().stream().collect(Collectors.toMap(AppoinMainWorkUser::getId, o -> o, (o1, o2) -> o1));
        List<Map<String, Object>> detailList = appoinTimeDetailService.listMaps(new QueryWrapper<AppoinTimeDetail>()
                .in("APPOIN_TIME_MAIN_ID_", workUserMap.keySet())
                .groupBy("APPOIN_TIME_MAIN_ID_")
                .select("APPOIN_TIME_MAIN_ID_ mainId", "COUNT(1) count")
        );
        detailList.forEach(o -> {
            workUserMap.get(o.get("mainId").toString()).setWorkTimeCount(Integer.valueOf(o.get("count").toString()));
        });
        return CollUtil.newArrayList(appoinMainWorkUserMap.values());
    }

    @Override
    public List<AppoinMainCalendar> queryByInterval(String startDate, String endDate) {
        List<AppoinMainCalendar> appoinMainCalendars = super.list(new QueryWrapper<AppoinTimeMain>()
                .between("appoin_date_", startDate, endDate))
                .stream().map(o ->
                        AppoinMainCalendar.builder()
                                .id(o.getId())
                                .title(o.getUserCode())
                                .editable(false)
                                .start(o.getAppoinDate())
                                .backgroundColor(o.getIsActive() ? null : "#CDCDCD")
                                .borderColor(o.getIsActive() ? null : "#CDCDCD")
                                .build()).collect(Collectors.toList());
        Set<String> userCodes = appoinMainCalendars.stream().map(o -> o.getTitle()).collect(Collectors.toSet());
        Map<String, TeacherInfoVO> teacherInfoVOMap = teacherInter.findByWorkNos(CollUtil.newArrayList(userCodes));
        appoinMainCalendars.stream().forEach(o -> {
            if (teacherInfoVOMap.containsKey(o.getTitle())) {
                o.setTitle(teacherInfoVOMap.get(o.getTitle()).getName());
            }
        });
        return appoinMainCalendars;
    }

    @Override
    public Map<String, Integer> queryCountByInterval(String startDate, String endDate) {
        List<AppoinTimeMain> appoinTimeMains = super.list(new QueryWrapper<AppoinTimeMain>()
                .eq("is_active_", true)
                .between("appoin_date_", startDate, endDate)
                .orderByAsc("appoin_date_"));
        Map<String, Integer> buildMap = Maps.newHashMap();
        appoinTimeMains.stream().forEach(o -> {
            Integer integer = appoinTimeDetailService.queryAvailableCountForMain(o.getId());
            String appoinDate = DateUtil.format(o.getAppoinDate(), "yyyy-MM-dd");
            Integer availableCount = integer;
            if (buildMap.containsKey(appoinDate)) {
                availableCount += buildMap.get(appoinDate);
            }
            buildMap.put(appoinDate, availableCount);
        });
        return buildMap;
    }

    @Override
    @Deprecated
    public AppoinTimeMain queryMax(Long tenantId) {
        Page<AppoinTimeMain> page = new Page<>(1, 1);
        super.page(page, new QueryWrapper<AppoinTimeMain>()
                .eq("IS_ACTIVE_", true)
                .eq("TENANT_ID", tenantId)
                .orderByDesc("APPOIN_DATE_"));
        if (!page.getRecords().isEmpty()) {
            return page.getRecords().get(0);
        }
        return null;
    }

    @Override
    public List<AppoinMainWorkUser> queryByWorkUser(String userCode, String startDate, String endDate) {
        Map<String, AppoinMainWorkUser> appoinMainWorkUserMap = this.baseMapper.queryByWorkUser(userCode, startDate, endDate)
                .stream().collect(Collectors.toMap(AppoinTimeMain::getId, o -> {
                    AppoinMainWorkUser appoinMainWorkUser = new AppoinMainWorkUser();
                    BeanUtils.copyProperties(o, appoinMainWorkUser);
                    return appoinMainWorkUser;
                }, (o1, o2) -> o1));
        if (appoinMainWorkUserMap.size() == 0) {
            return CollUtil.newArrayList();
        }
        // 查询工作时间数量
        List<Map<String, Object>> detailList = appoinTimeDetailService.listMaps(new QueryWrapper<AppoinTimeDetail>()
                .in("APPOIN_TIME_MAIN_ID_", appoinMainWorkUserMap.keySet())
                .groupBy("APPOIN_TIME_MAIN_ID_")
                .select("APPOIN_TIME_MAIN_ID_ mainId", "COUNT(1) count")
        );
        detailList.forEach(o -> {
            appoinMainWorkUserMap.get(o.get("mainId").toString()).setWorkTimeCount(Integer.valueOf(o.get("count").toString()));
        });
        return CollUtil.newArrayList(appoinMainWorkUserMap.values());
    }

    @Override
    public List<AppoinMainWorkUser> queryByWorkUser2(String userCode, Date startDate, Date endDate) {
        return super.list(new QueryWrapper<AppoinTimeMain>()
                .eq("USER_CODE_", userCode)
                .eq("IS_ACTIVE_", true)
                .between("APPOIN_DATE_", startDate, endDate))
                .stream().map(o -> {
                    AppoinMainWorkUser appoinMainWorkUser = new AppoinMainWorkUser();
                    BeanUtils.copyProperties(o, appoinMainWorkUser);
                    return appoinMainWorkUser;
                }).collect(Collectors.toList());
    }

    @Override
    public Collection<AppoinTimeMain> saveWorkUser(Long tenantId, AppoinTimeMainSaveDTO appoinTimeMainSaveDTO) {
        Collection<AppoinTimeMain> appoinTimeMains = new ArrayList<>();
        for (String userCode : appoinTimeMainSaveDTO.getUserCodes()) {
            appoinTimeMains.add(AppoinTimeMain.builder()
                    .appoinDate(appoinTimeMainSaveDTO.getAppoinDate())
                    .userCode(userCode)
                    .isActive(false)
                    .tenantId(tenantId)
                    .build());
        }
        super.saveBatch(appoinTimeMains);
        return appoinTimeMains;
    }

    @Override
    public void toggleWorkState(String id) {
        AppoinTimeMain appoinTimeMain = super.getById(id);
        super.update(new UpdateWrapper<AppoinTimeMain>()
                .eq("ID_", id)
                .set("IS_ACTIVE_", !appoinTimeMain.getIsActive())
        );
    }

    @Override
    public List<AppoinMainCalendar> queryByIntervalAndUser(String userCode, String startDate, String endDate) {
        List<AppoinMainCalendar> appoinMainCalendars = Lists.newArrayList();
        // 当前启用的总设置
        AppoinTimeRuleEntity appoinTimeRuleEntity = appoinTimeRuleService.queryUse();
        if (appoinTimeRuleEntity == null) {
            throw new RRException("未开启默认规则");
        }
        // 周设置
        List<AtrWeekEntity> atrWeekEntities = atrWeekService.list(new QueryWrapper<AtrWeekEntity>()
                .eq("RULE_ID_", appoinTimeRuleEntity.getId()));
        // 总例外
        List<AtrSpecialEntity> atrSpecialEntities = atrSpecialService.list(new QueryWrapper<AtrSpecialEntity>()
                .eq("RULE_ID_", appoinTimeRuleEntity.getId())
                .between("SPECIAL_DATE_", startDate, endDate));
        // 个人例外
        AtRule atRule = atRuleService.queryByUserCode(userCode);
        List<AtSpecial> atSpecials = atSpecialService.queryByUserAndInterval(startDate, endDate, atRule.getId());
        // 处理数据
        Calendar calendarStart = DateUtil.parse(startDate).toCalendar();
        Calendar calendarEnd = DateUtil.parse(endDate).toCalendar();
        AppoinWorkVO appoinWorkVO;
        StringBuilder titleBuilder = null;
        while (calendarStart.before(calendarEnd)) {
            appoinWorkVO = this.findWorktime(calendarStart, atSpecials, atrSpecialEntities, atrWeekEntities);
            titleBuilder = new StringBuilder();
            titleBuilder.append(appoinWorkVO.getTitle());
            if (appoinWorkVO.getIsWork()) {
                for (AppoinWorkTimeVO appoinWorkTime : appoinWorkVO.getAppoinWorkTimes()) {
                    titleBuilder.append("\r\n").append(appoinWorkTime.getStartTime()).append(" ~ ")
                            .append(appoinWorkTime.getEndTime());
                }
            }
            appoinMainCalendars.add(AppoinMainCalendar.builder()
                    .title(titleBuilder.toString())
                    .backgroundColor(appoinWorkVO.getIsWork() ? "#a3cf62" : "#fcaf17")
                    .start(calendarStart.getTime())
                    .build());
            calendarStart.add(Calendar.DATE, 1);
        }
        return appoinMainCalendars;
    }

    @Override
    public AppoinTimeMain queryByDateAndUser(String date, String userCode) {
        return super.getOne(new QueryWrapper<AppoinTimeMain>()
                .eq("APPOIN_DATE_", date)
                .eq("USER_CODE_", userCode));
    }

    private AppoinWorkVO findWorktime(Calendar calendar, List<AtSpecial> atSpecials, List<AtrSpecialEntity> atrSpecials,
                                      List<AtrWeekEntity> atrWeeks) {
        AppoinWorkVO appoinWork = null;
        for (AtSpecial atSpecial : atSpecials) {
            if (atSpecial.getSpecialDate().compareTo(calendar.getTime()) == 0) {
                // 当前日期设置了个人例外
                appoinWork = new AppoinWorkVO();
                appoinWork.setIsWork(atSpecial.getIsWork());
                appoinWork.setTitle(atSpecial.getTitle());
                if (appoinWork.getIsWork()) {
                    List<AtSpecialWorkTime> atSpecialWorkTimes = atSpecialWorkTimeService.findBySpecial(atSpecial.getId());
                    AppoinWorkTimeVO appoinWorkTime = null;
                    for (AtSpecialWorkTime atSpecialWorkTime : atSpecialWorkTimes) {
                        appoinWorkTime = new AppoinWorkTimeVO();
                        appoinWorkTime.setStartTime(atSpecialWorkTime.getStartTime());
                        appoinWorkTime.setEndTime(atSpecialWorkTime.getEndTime());
                        appoinWork.getAppoinWorkTimes().add(appoinWorkTime);
                    }
                }
                return appoinWork;
            }
        }
        for (AtrSpecialEntity atrSpecial : atrSpecials) {
            if (atrSpecial.getSpecialDate().getTime() == calendar.getTimeInMillis()) {
                appoinWork = new AppoinWorkVO();
                appoinWork.setIsWork(atrSpecial.getIsWork());
                appoinWork.setTitle(atrSpecial.getTitle());
                if (appoinWork.getIsWork()) {
                    List<AtrSpecialWorkTime> atrSpecialWorkTimes = atrSpecialWorkTimeService.findBySpecial(atrSpecial.getId());
                    AppoinWorkTimeVO appoinWorkTime = null;
                    for (AtrSpecialWorkTime atrSpecialWorkTime : atrSpecialWorkTimes) {
                        appoinWorkTime = new AppoinWorkTimeVO();
                        appoinWorkTime.setStartTime(atrSpecialWorkTime.getStartTime());
                        appoinWorkTime.setEndTime(atrSpecialWorkTime.getEndTime());
                        appoinWork.getAppoinWorkTimes().add(appoinWorkTime);
                    }
                }
                return appoinWork;
            }
        }
        for (AtrWeekEntity atrWeek : atrWeeks) {
            if (atrWeek.getWeekDay() == calendar.get(Calendar.DAY_OF_WEEK)) {
                appoinWork = new AppoinWorkVO();
                appoinWork.setIsWork(atrWeek.getIsWork());
                appoinWork.setTitle(atrWeek.getIsWork() ? "工作日" : "非工作日");
                if (appoinWork.getIsWork()) {
                    List<AtrWeekWorkTimeEntity> atrWeekWorkTimes = atrWeekWorkTimeService.findByWeek(atrWeek.getId());
                    AppoinWorkTimeVO appoinWorkTime = null;
                    for (AtrWeekWorkTimeEntity atrWeekWorkTime : atrWeekWorkTimes) {
                        appoinWorkTime = new AppoinWorkTimeVO();
                        appoinWorkTime.setStartTime(atrWeekWorkTime.getStartTime());
                        appoinWorkTime.setEndTime(atrWeekWorkTime.getEndTime());
                        appoinWork.getAppoinWorkTimes().add(appoinWorkTime);
                    }
                }
                return appoinWork;
            }
        }
        appoinWork = new AppoinWorkVO();
        appoinWork.setIsWork(false);
        appoinWork.setTitle("无配置");
        return appoinWork;
    }
}
